package com.tc.property.config;

import com.tc.property.config.handler.*;
import com.tc.property.config.impl.ClasspathFileConfig;
import com.tc.property.config.impl.FileConfig;
import com.tc.property.config.impl.SysEnvConfig;
import com.tc.property.config.model.ConfigContext;
import com.tc.property.config.model.ConfigItem;
import com.tc.property.config.zk.CfgCenterConfig;
import com.tc.property.util.ConfigPropertyPlaceholderHelper;
import com.tc.property.util.Constants;
import com.tc.property.util.Utils;
import com.tc.zookeeper.ZKConstants;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.Map.Entry;

public class ConfigLoader {

	private static final String CONFIG_DEFAULT = "classpath:/properties/application.properties,classpath:/properties/${spring.profiles.default}/application-${spring.profiles.default}.properties,classpath:/properties/${spring.profiles.active}/application-${spring.profiles.active}.properties,file:${appPropertiesFile},sysenv,cfgcenter:${app.name}:${path}";

	private static final String CONFIG_DELIMITERS = ",; \t\n";

	private List<IConfigChangedHandler> handlers = new LinkedList<IConfigChangedHandler>();

	private ConfigContext configContext = new ConfigContext();

	private Boolean ignoreResourceNotFound = true;
	
	private Boolean isReportEvent = true;


	private static ConfigPropertyPlaceholderHelper propertyPlaceholderHelper = new ConfigPropertyPlaceholderHelper(
			Constants.DEFAULT_PLACEHOLDER_PREFIX, Constants.DEFAULT_PLACEHOLDER_SUFFIX,
			Constants.DEFAULT_VALUE_SEPARATOR, true);

	public ConfigContext initConfig() {
		String[] sources = StringUtils.split(CONFIG_DEFAULT, CONFIG_DELIMITERS);
		return initConfigFromSources(Arrays.asList(sources));
	}

	public ConfigContext initConfig(List<IConfigChangedHandler> handlers) {
		this.handlers = handlers;
		return initConfig();
	}

	public ConfigContext initConfigFromSources(List<String> sources) {
		if (sources != null) {
			boolean readSysEnv = true;
			Map<String, ConfigItem> sysEnvConfigItems = new SysEnvConfig(Constants.SYSENV).getConfig();
			configContext.merge(sysEnvConfigItems);
			for (final String source : sources) {
				if (source.contains(Constants.CLASSPATH_PREFIX)) {
					String classPathUrl = getFinalSource(source);
					configContext.merge(new ClasspathFileConfig(classPathUrl, ignoreResourceNotFound).getConfig());

					if (readSysEnv) {
						// merge sys env property
						configContext.merge(sysEnvConfigItems);
					}
				} else if (source.contains(Constants.FILE_PREFIX)) {
					String filePath = getFinalSource(source);
					List<IConfigSourceChangedHandler> sourceChangeHandlers = new LinkedList<IConfigSourceChangedHandler>();
					sourceChangeHandlers.add(new DefaultConfigSourceChangedHandler(configContext, handlers));
					configContext.merge(new FileConfig(filePath, ignoreResourceNotFound)
							.getConfig(sourceChangeHandlers));

					if (readSysEnv) {
						// merge sys env property
						configContext.merge(sysEnvConfigItems);
					}
				} else if (source.contains(Constants.CFGCENRTER_PREFIX)) {
					String zkPathUrl = getFinalSource(source);
					List<IConfigSourceChangedHandler> sourceChangeHandlers = new LinkedList<IConfigSourceChangedHandler>();
					sourceChangeHandlers.add(new DefaultConfigSourceChangedHandler(configContext, handlers));

					String zkConnectString = configContext.get(ZKConstants.VIP_CFGCENTER_ZK_CONNECTION);
					Map<String, ConfigItem> cfgcenterItemMap = new CfgCenterConfig(zkPathUrl, zkConnectString,
							ignoreResourceNotFound).getConfig(sourceChangeHandlers);
					configContext.merge(cfgcenterItemMap);
					configContext.setCfgcenterItemMap(cfgcenterItemMap);

					if (readSysEnv) {
						configContext.merge(sysEnvConfigItems);
					}
				} else if (source.contains(Constants.SYSENV)) {
					configContext.merge(sysEnvConfigItems);
					readSysEnv = false;
				}
			}
		}

		return replacePlaceholders(configContext);//解析表达式 ${haha}
	}
	
	public ConfigContext initConfigFromSourcesByBootStrap(List<String> sources) {
		if (sources != null) {
			boolean readSysEnv = true;
			Map<String, ConfigItem> sysEnvConfigItems = new SysEnvConfig(Constants.SYSENV).getConfig();
			configContext.merge(sysEnvConfigItems);
			for (final String source : sources) {
				if (source.contains(Constants.CLASSPATH_PREFIX)) {
					String classPathUrl = getFinalSource(source);
					configContext.merge(new ClasspathFileConfig(classPathUrl, ignoreResourceNotFound).getConfig());

					if (readSysEnv) {
						// merge sys env property
						configContext.merge(sysEnvConfigItems);
					}
				} else if (source.contains(Constants.FILE_PREFIX)) {
					String filePath = getFinalSource(source);
					List<IConfigSourceChangedHandler> sourceChangeHandlers = new LinkedList<IConfigSourceChangedHandler>();
					sourceChangeHandlers.add(new DefaultConfigSourceChangedHandler(configContext, handlers));
					configContext.merge(new FileConfig(filePath, ignoreResourceNotFound).getConfig(sourceChangeHandlers));

					if (readSysEnv) {
						// merge sys env property
						configContext.merge(sysEnvConfigItems);
					}
				} else if (source.contains(Constants.CFGCENRTER_PREFIX)) {
					String zkPathUrl = getFinalSource(source);
					List<IConfigSourceChangedHandler> sourceChangeHandlers = new LinkedList<IConfigSourceChangedHandler>();
					sourceChangeHandlers.add(new DefaultConfigSourceChangedHandler(configContext, handlers));
					Map<String, ConfigItem> cfgcenterItemMap = new CfgCenterConfig(zkPathUrl, ignoreResourceNotFound, isReportEvent).getConfig(sourceChangeHandlers);
					configContext.merge(cfgcenterItemMap);
					configContext.setCfgcenterItemMap(cfgcenterItemMap);

					if (readSysEnv) {
						configContext.merge(sysEnvConfigItems);
					}
				} else if (source.contains(Constants.SYSENV)) {
					configContext.merge(sysEnvConfigItems);
					readSysEnv = false;
				}
			}
		}

		return replacePlaceholders(configContext);
	}


	private static ConfigContext replacePlaceholders(ConfigContext configContext) {
		Properties props = Utils.getPropsFromContext(configContext);

		Set<Entry<String, ConfigItem>> entrySet = configContext.getContextItemMap().entrySet();
		for (Entry<String, ConfigItem> entry : entrySet) {
			ConfigItem configItem = entry.getValue();
			String value = configItem.getValue();
			if (StringUtils.isNotEmpty(value) && value.contains(Constants.DEFAULT_PLACEHOLDER_PREFIX)) {
				value = propertyPlaceholderHelper.replacePlaceholders(value, props);
				configItem.setValue(value);
				configContext.getContextItemMap().put(entry.getKey(), configItem);

				// if (value.contains(Constants.DEFAULT_PLACEHOLDER_PREFIX)) {
				// LOGGER.error(value + " can not be replaced.");
				// }
			}
		}

		return configContext;
	}

	private String getFinalSource(String source) {
		String finalSource = propertyPlaceholderHelper.replacePlaceholders(source,
				Utils.getPropsFromContext(configContext));
		return finalSource;
	}

	public ConfigContext initConfigFromSources(List<String> sources, List<IConfigChangedHandler> handlers) {
		this.handlers = handlers;
		return initConfigFromSources(sources);
	}
	
	public ConfigContext initConfigFromSourcesByBootstrap(List<String> sources, List<IConfigChangedHandler> handlers, Boolean isReportEvent) {
		this.handlers = handlers;
		this.isReportEvent = isReportEvent;
		return initConfigFromSourcesByBootStrap(sources);
	}

	public ConfigContext initConfigFromSources(List<String> sources, List<IConfigChangedHandler> handlers,
			Boolean ignoreResourceNotFound) {
		this.handlers = handlers;
		this.ignoreResourceNotFound = ignoreResourceNotFound;
		return initConfigFromSources(sources);
	}
}
